package cn.laixueit.cart.service;

import cn.laixueit.cart.client.BrandClient;
import cn.laixueit.cart.client.CategoryClient;
import cn.laixueit.cart.client.GoodsClient;
import cn.laixueit.cart.client.SpecClient;
import cn.laixueit.cart.pojo.Goods;
import cn.laixueit.cart.pojo.SearchRequest;
import cn.laixueit.cart.pojo.SearchResult;
import cn.laixueit.cart.pojo.item.*;
import cn.laixueit.repositry.GoodsRepositry;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.math.NumberUtils;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.MatchQueryBuilder;
import org.elasticsearch.index.query.Operator;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.bucket.terms.LongTerms;
import org.elasticsearch.search.aggregations.bucket.terms.StringTerms;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.elasticsearch.core.aggregation.AggregatedPage;
import org.springframework.data.elasticsearch.core.query.FetchSourceFilter;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.io.IOException;
import java.util.*;

@Service
public class SearchService {

    @Autowired
    private BrandClient brandClient;

    @Autowired
    private CategoryClient categoryClient;

    @Autowired
    private GoodsClient goodsClient;

    @Autowired
    private SpecClient specClient;

    @Autowired
    private GoodsRepositry goodsRepositry;

    private final ObjectMapper MAPPER = new ObjectMapper();

    public SearchResult page(SearchRequest searchRequest) {
        SearchResult result = new SearchResult();

        NativeSearchQueryBuilder builder = new NativeSearchQueryBuilder();

        BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();

        MatchQueryBuilder matchQueryBuilder = QueryBuilders.matchQuery("all", searchRequest.getKey()).operator(Operator.AND);
        boolQuery.must(matchQueryBuilder);

        if(!CollectionUtils.isEmpty(searchRequest.getFilter())){
            Set<Map.Entry<String, String>> entrySet = searchRequest.getFilter().entrySet();
            for (Map.Entry<String, String> entry : entrySet) {
                String key = "";
                if(StringUtils.equals(entry.getKey(),"品牌") ){
                     key="brandId";
                }else if(StringUtils.equals(entry.getKey(),"分类") ){
                    key="cid3";
                }else{
                    key="specs."+entry.getKey()+".keyword";
                }
                boolQuery.filter(QueryBuilders.termQuery(key, entry.getValue()));
            }
        }

        builder.withQuery(boolQuery);

        // 分页
        builder.withPageable(PageRequest.of((searchRequest.getPage()-1),searchRequest.getSize()));

        // 字段过滤
        builder.withSourceFilter(new FetchSourceFilter(new String[]{"id","skus","subTitle"},null));

        // 分类查询
        String brandAggName = "brands";
        String categoryAggName = "categorys";
        builder.addAggregation(AggregationBuilders.terms(brandAggName).field("brandId"));
        builder.addAggregation(AggregationBuilders.terms(categoryAggName).field("cid3"));

        AggregatedPage<Goods> goodsAgg = (AggregatedPage<Goods>) goodsRepositry.search(builder.build());

        LongTerms brandIdsTerms = (LongTerms) goodsAgg.getAggregation(brandAggName);
        List<Brand> brandList = getBrandAggResult(brandIdsTerms);

        LongTerms categorysTerms = (LongTerms) goodsAgg.getAggregation(categoryAggName);
        List<Map<String, Object>> maps = getCategoryAggReslt(categorysTerms);

        List<Map<String, Object>> paramList = null;
        if(maps.size()==1){
            Long cid3 = (Long) maps.get(0).get("id");
            paramList = getParamAggResult(cid3, matchQueryBuilder);
        }

        result.setTotal(goodsAgg.getTotalElements());
        result.setItems(goodsAgg.getContent());
        result.setTotalPage(Long.valueOf(goodsAgg.getTotalPages()) );
        result.setCategorys(maps);
        result.setBrands(brandList);
        result.setSpecs(paramList);

        return result;
    }


    private ArrayList<Map<String, Object>> getParamAggResult(Long cid3,MatchQueryBuilder matchquerybuilder){

        ArrayList<Map<String, Object>> specsListMap = new ArrayList<>();
        // 创建自定义查询条件
        NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();
        queryBuilder.withQuery(matchquerybuilder);

        // 查询聚合的的规格参数
        Collection<SpecParams> specParams = specClient.groups(null, cid3, true,false).getBody();

        specParams.forEach(param->{
            queryBuilder.addAggregation(AggregationBuilders.terms(param.getName()).field("specs."+param.getName()+".keyword"));
        });

        queryBuilder.withSourceFilter(new FetchSourceFilter(new String[]{},null));

        AggregatedPage<Goods> goodsAgg = (AggregatedPage<Goods>) goodsRepositry.search(queryBuilder.build());

        Map<String, Aggregation> asMap = goodsAgg.getAggregations().asMap();

        for (Map.Entry<String, Aggregation> entry : asMap.entrySet()) {
            HashMap<String, Object> map = new HashMap<>();

            map.put("k",entry.getKey());

            ArrayList<Object> options = new ArrayList<>();
            StringTerms stringTerms = (StringTerms) entry.getValue();
            stringTerms.getBuckets().forEach(bucket -> options.add(bucket.getKeyAsString()));
            map.put("options",options);

            specsListMap.add(map);
        }
        return specsListMap;
    }

    private List<Map<String, Object>> getCategoryAggReslt(LongTerms cidTerms) {
        ArrayList<Map<String, Object>> list = new ArrayList<>();
        List<LongTerms.Bucket> buckets = cidTerms.getBuckets();
        ArrayList<Long> cids = new ArrayList<>();
        buckets.stream().forEach(bucket -> cids.add(bucket.getKeyAsNumber().longValue()));

        List<String> names = categoryClient.names(cids).getBody();
        for (int i = 0; i < cids.size(); i++) {
            HashMap<String, Object> map = new HashMap<>();
            map.put("id",cids.get(i));
            map.put("name",names.get(i));
            list.add(map);
        }
        return list;
    }

    private List<Brand> getBrandAggResult(LongTerms longTerms){
        ArrayList<Brand> brands = new ArrayList<>();
        List<LongTerms.Bucket> buckets = longTerms.getBuckets();
        for (LongTerms.Bucket bucket : buckets) {
            long bid = bucket.getKeyAsNumber().longValue();
            // 根据品牌id查询你品牌
            ResponseEntity<Brand> brand = brandClient.getBrandById(bid);
            brands.add(brand.getBody());
        }
        return brands;
    }

    public Goods buildGoods(Spu spu) throws IOException {


        Goods goods = new Goods();

        goods.setId(spu.getId());
        goods.setBrandId(spu.getBrandId());
        goods.setCid1(spu.getCid1());
        goods.setCid2(spu.getCid2());
        goods.setCid3(spu.getCid3());
        goods.setCreateTime(spu.getCreateTime());
        goods.setSubTitle(spu.getSubTitle());

        Brand brand = brandClient.getBrandById(spu.getBrandId()).getBody();

//        Category category = categoryClient.getCategoryById(spu.getCid3()).getBody();

        List<String> names = categoryClient.names(Arrays.asList(spu.getCid1(), spu.getCid2(), spu.getCid3())).getBody();

        goods.setAll(spu.getTitle()+" "+brand.getName()+" "+ StringUtils.join(names," "));

        ArrayList<Long> prices = new ArrayList<>();

        List<Sku> skus = goodsClient.list(spu.getId()).getBody();
        ArrayList<Map<String, Object>> skuListMap = new ArrayList<>();
        skus.forEach(sku -> {
            prices.add(sku.getPrice());
            HashMap<String, Object> skuMap = new HashMap<>();
            skuMap.put("id",sku.getId());
            skuMap.put("title",sku.getTitle());
            skuMap.put("price",sku.getPrice());
            skuMap.put("image",StringUtils.isNotBlank(sku.getImages())?sku.getImages().split(",")[0]:"");
            skuListMap.add(skuMap);
        });

        goods.setPrices(prices);

        goods.setSkus(MAPPER.writeValueAsString(skuListMap));

        Collection<SpecParams> specParams = specClient.groups(null, spu.getCid3(), true,null).getBody();

        SpuDetail spuDetail = goodsClient.detail(spu.getId()).getBody();

        Map<String,Object> genericSpecMap = MAPPER.readValue(spuDetail.getGenericSpec(),new TypeReference<Map<String,Object>>(){}) ;

        Map<String,List>  specialSpecMap =  MAPPER.readValue(spuDetail.getSpecialSpec(),new TypeReference<Map<String,List>>(){});

        HashMap<String, Object> paramMap = new HashMap<>();
        specParams.forEach(param->{
            if(param.getGeneric()){
                Long id = param.getId();
                Object o = genericSpecMap.get(id.toString());
                String value  = o.toString();
//                String value = genericSpecMap.get(param.getId()).toString();
               if(param.getNumeric()){
                    value = chooseSegment(value,param);
               }
               paramMap.put(param.getName(),value);
            }else{
                paramMap.put(param.getName(),specialSpecMap.get(param.getId().toString()));
            }
        });

        goods.setSpecs(paramMap);

        return goods;
    }


    private String chooseSegment(String value, SpecParams p) {
        double val = NumberUtils.toDouble(value);
        String result = "其它";
        // 保存数值段
        for (String segment : p.getSegments().split(",")) {
            String[] segs = segment.split("-");
            // 获取数值范围
            double begin = NumberUtils.toDouble(segs[0]);
            double end = Double.MAX_VALUE;
            if(segs.length == 2){
                end = NumberUtils.toDouble(segs[1]);
            }
            // 判断是否在范围内
            if(val >= begin && val < end){
                if(segs.length == 1){
                    result = segs[0] + p.getUnit() + "以上";
                }else if(begin == 0){
                    result = segs[1] + p.getUnit() + "以下";
                }else{
                    result = segment + p.getUnit();
                }
                break;
            }
        }
        return result;
    }


    public void saveOrUpDate(Long id) {
        try {
            Spu spu = goodsClient.getSpuById(id).getBody();
            Goods goods = buildGoods(spu);
            goodsRepositry.save(goods);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void delete(Long id) {
        goodsRepositry.deleteById(id);
    }
}
